#ifndef xpack_lang_cxx_replace
#define xpack_lang_cxx_replace
#pragma push_macro("xuser")
#undef  xuser
#define xuser mixc::lang_cxx::inc
#include"algo/var_array.hpp"
#include"configure/limit.hpp"
#include"define/base_type.hpp"
#include"lang/cxx/find.hpp"
#include"lang/cxx.hpp"
#include"macro/xdefer.hpp"
#include"macro/xunlikely.hpp"
#include"memop/copy.hpp"
#include"utils/memory.hpp"
#pragma pop_macro("xuser")

namespace mixc::lang_cxx{
    template<class item_t>
    template<class xalloc_t, class ret_t, class>
    inline ret_t cxx<item_t>::replace(the_cref_t old_value, the_cref_t new_value, xalloc_t const & alloc) const {
        return xthe.replace(old_value, new_value, inc::default_compare<item_t>, alloc);
    }

    template<class item_t>
    inline cxx<item_t> cxx<item_t>::replace(
        the_cref_t          old_value, 
        the_cref_t          new_value, 
        invoke_compare_t    compare, 
        alloc_t             alloc
    ) const {
        using var           = inc::var_array<inc::limit::lang_cxx_replace, inc::with_fixed_page_table>;

        uxx     buffer[inc::limit::lang_cxx_replace];
        uxx *   table[40];
        uxx **  page_table  = table;
        uxx     count       = 0;
        uxx     last_index  = 0;
        bool    once        = true;

        // 融合栈内存和堆内存
        auto allocx = [&](uxx bytes) -> voidp {
            if (once){
                once        = false;
                return buffer;
            }
            else{
                return inc::memory::alloc<void>(inc::memory::size{bytes});
            }
        };

        auto freex = [&](voidp ptr, uxx bytes){
            if (ptr == voidp(buffer)){
                return;
            }
            inc::memory::free(ptr, inc::memory::size{bytes});
        };

        xdefer{
            var::clear(& page_table, & count, freex);
        };

        xthe.find(old_value, compare, [&](uxx offset){
            var::push(& page_table, & count, offset - last_index, allocx, freex);
            last_index      = offset + old_value->length;
        });

        auto dis            = new_value->length - old_value->length;
        auto len_this       = uxx(xthe->length);
        auto len_old        = uxx(old_value->length);
        auto len_new        = uxx(new_value->length);
        auto len            = len_this + dis * count;
        auto buf            = alloc(len);

        xunlikely(buf == nullptr){
            return {};
        }

        auto ptr            = buf;
        auto ptr_origin     = static_cast<item_t *>(xthe);
        auto ptr_new        = static_cast<item_t *>(new_value);
        auto offset         = uxx{};
        auto copy_size      = uxx{};

        for(uxx i = 0; i < count; i++){
            offset          = var::access(page_table, i);
            copy_size       = offset;
            inc::copy_with_operator(ptr, ptr_origin, copy_size);
            ptr            += offset;
            ptr_origin     += offset + len_old;

            if (len_new != 0){
                inc::copy_with_operator(ptr, ptr_new, len_new);
                ptr        += len_new;
            }
        }

        inc::copy_with_operator(ptr, ptr_origin, len_this - last_index);
        return the_t(buf, len);
    }


    template<class item_t>
    inline void cxx<item_t>::replace(
        the_cref_t          old_value, 
        the_cref_t          new_value, 
        invoke_compare_t    compare, 
        stream_t            alloc
    ) const {
        auto i_last         = uxx{};
        auto stream         = alloc(0);
        xthe.find(old_value, compare, [&](uxx offset){
            if (i_last != offset){
                stream.output(m_ptr + i_last, offset - i_last);
            }

            stream.output(new_value.m_ptr, new_value.m_length);
            i_last          = offset + old_value.m_length;
        });

        if (i_last != m_length){
            stream.output(m_ptr + i_last, m_length - i_last);
        }
    }
}

#endif
